Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(behaviors): Add behavior metadata information. #2231

Merged

Conversation

petejohanson
Copy link
Contributor

  • For upcoming ZMK studio work, make a set of rich metadata available to provide a friendly name for a behavior, and allow super flexible descriptions of the parameters the behaviors take.

@petejohanson petejohanson added enhancement New feature or request behaviors studio ZMK Studio (runtime keymaps) labels Mar 27, 2024
@petejohanson petejohanson self-assigned this Mar 27, 2024
@petejohanson petejohanson requested a review from a team as a code owner March 27, 2024 22:23
@@ -9,6 +9,7 @@
/omit-if-no-ref/ to: to_layer {
compatible = "zmk,behavior-to-layer";
#binding-cells = <1>;
friendly-name = "Go To Layer";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't call this "Go To Layer" anywhere in the docs, just "To Layer". I think it'd be good to be consistent, do you prefer the former?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I was reading it.. I realized it was a pretty odd name. I'll update to match our current language.

@@ -9,6 +9,7 @@
/omit-if-no-ref/ tog: toggle_layer {
compatible = "zmk,behavior-toggle-layer";
#binding-cells = <1>;
friendly-name = "Layer Toggle";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also typically called "Toggle Layer." Having a proper noun phrase rather than verb+action might be better in general, but I'd again tend to consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll defer to consistency for now, but I do think this reads better than "Toggle Layer", IMHO.

.position = 0,
.value = BT_NXT_CMD,
.friendly_name = "Next Profile",
.type = BEHAVIOR_PARAMETER_VALUE_METADATA_TYPE_VALUE,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think it would be easier to read these if the type came before the value/range/etc. Maybe this would be a good order?

  1. Position
  2. Name
  3. Type
  4. Value/range/etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Definitely reads better, thanks!

app/include/drivers/behavior.h Outdated Show resolved Hide resolved
@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 2 times, most recently from e968c3a to 0119f0b Compare March 29, 2024 21:34
@petejohanson petejohanson mentioned this pull request Apr 5, 2024
3 tasks
@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 2 times, most recently from fda849e to a9d724f Compare April 10, 2024 20:54
app/include/drivers/behavior.h Outdated Show resolved Hide resolved
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_NULL = 0,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HID_USAGE = 1,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX = 2,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV = 3,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV = 3,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_COLOR_HSV = 3,

might make it clearer what HSV means. (If it isn't supposed to be a color, then this really needs to be clarified.)

Also, RGB seems like a generally more common format for sending color data. I guess we could add a *_COLOR_RGB value later, but then we'd have to deal with two different color formats everywhere.

One alternative would be to change the underglow code to store the value as RGB, then convert to HSV when loading. If the devicetree format needs to match, we could also change RGB_COLOR_HSB_VAL() to convert from HSV to RGB.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had originally used "HSV" as the "native" format to make it easier to do increase/decrease brightness, IIRC. I don't have a strong opinion here, tbh. Maybe @Nicell has soe thoughts though.

Copy link
Collaborator

@joelspadin joelspadin Jun 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is that you can only represent a small fraction of the possible RGB colors using integer HSV values. Assuming I correctly wrote the Python script I used to test this, rgb_underglow.c's hsb_to_rgb() function can represent 2,189,687 out of 16,777,216 possible colors (about 13%).

app/include/drivers/behavior.h Show resolved Hide resolved
app/include/drivers/behavior.h Outdated Show resolved Hide resolved
app/include/drivers/behavior.h Show resolved Hide resolved
@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 2 times, most recently from 29be027 to cd1c6d0 Compare June 5, 2024 07:54
@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 8 times, most recently from e5b4818 to 0598671 Compare June 12, 2024 21:50
};

enum {
BEHAVIOR_PARAMETER_VALUE_METADATA_TYPE_VALUE = 0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need "metadata" here? This is the type of the parameter value, not the type of the metadata itself, so this naming could be a little confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened.

@@ -209,9 +213,98 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

static int get_macro_parameter_metadata(const struct device *macro,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is massive. Any way it could be broken into smaller pieces?

The combination of loop conditions with param1_found/param2_found and continue/break statements is particularly difficult to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, cleaned this up a ton, after properly identifying the edges. Read better?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might warrant some extra comments explaining exactly what this is doing, but I think I correctly understand what it's doing now, so definitely an improvement.

Just to be sure, this is looping through the list of bindings in the macro, looking for the first thing it can find that uses each of the macro parameters, then returning a combination of the sets for each of those? I'm not sure I understand why the logic for param1 is different than param1 though. Does the param1 case not need to check if the bound param2 is valid?

@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 4 times, most recently from 3a92c3b to 1057852 Compare June 13, 2024 17:12
@petejohanson petejohanson force-pushed the core/add-behavior-metadata branch 3 times, most recently from 276ad58 to 3949f90 Compare June 21, 2024 22:21
app/src/behavior.c Outdated Show resolved Hide resolved
app/src/behavior.c Outdated Show resolved Hide resolved
switch (value_meta->type) {
case BEHAVIOR_PARAMETER_VALUE_TYPE_NIL:
if (param == 0) {
found_match = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we unconditionally break from the loop and return 0 upon reaching any of these found_match = true cases, maybe we should just return 0 directly instead? Could add something like #define FOUND_MATCH 0 so this becomes return FOUND_MATCH; and stays clear that we're returning a value that indicates we found a match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, in a split out function. Thanks!

app/src/behavior.c Outdated Show resolved Hide resolved
return 0;
}

int zmk_behavior_validate_param_values(const struct behavior_parameter_value_metadata *values,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the name "validate param values", I first thought this was for validating the struct behavior_parameter_value_metadata array itself. Not sure how better to clarify this. Could add some documentation to explain it, or maybe rename it? Some ideas

  • zmk_behavior_matches_param_values()
  • zmk_behavior_validate_param_with_param_values()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to zmk_behavior_check_params_match_metadata for clarity. That work for you?


if (child_meta.sets_len > 0) {
data->set.param1_values = child_meta.sets[0].param1_values;
data->set.param1_values_len = child_meta.sets[0].param1_values_len;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it assumed that these are already zeroed out before calling this function, or is it intentional to leave it at its previous value in the case where the behavior_get_parameter_metadata() function returns an empty list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the assumption is the set's got a default zero'd state before this gets called.

@@ -209,9 +213,98 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

static int get_macro_parameter_metadata(const struct device *macro,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might warrant some extra comments explaining exactly what this is doing, but I think I correctly understand what it's doing now, so definitely an improvement.

Just to be sure, this is looping through the list of bindings in the macro, looking for the first thing it can find that uses each of the macro parameters, then returning a combination of the sets for each of those? I'm not sure I understand why the logic for param1 is different than param1 though. Does the param1 case not need to check if the bound param2 is valid?

* For upcoming ZMK studio work, make a set of rich metadata available
  to provide a friendly name for a behavior, and allow super flexible
  descriptions of the parameters the behaviors take.
* Add ability to validate a zmk_behavior_binding against
  the behavior metadata available.
@petejohanson petejohanson merged commit 03099b0 into zmkfirmware:main Jun 24, 2024
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
behaviors enhancement New feature or request studio ZMK Studio (runtime keymaps)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants